#!/usr/bin/env node

const fs = require("fs");
const path = require("path");
const { exec } = require("child_process");
const csvParser = require("csv-parser");
const { createObjectCsvWriter } = require("csv-writer");
const util = require("util");
const execPromise = util.promisify(exec);

/**
 * Main function to coordinate the process
 */
async function main() {
  try {
    // Parse command line arguments
    const args = parseArguments();
    console.log(`Reading student data from ${args.inputCsv}...`);

    // Read input files
    const students = await readCSV(args.inputCsv);
    console.log(`Loaded ${students.length} students`);

    const config = readConfig();
    console.log(
      `Loaded ${config.repositories.length} repositories from config.json`,
    );

    // Process student branches
    console.log(
      "Checking branch existence for each student across all repositories...",
    );
    const resultsWithBranchStatus = await checkBranchesForAllStudents(
      students,
      config.repositories,
    );

    // Write results to CSV
    await writeCSV(
      resultsWithBranchStatus,
      config.repositories,
      args.outputCsv,
    );
    console.log(
      `Process completed successfully! Results written to ${args.outputCsv}`,
    );
  } catch (error) {
    console.error("Error:", error.message);
    process.exit(1);
  }
}

/**
 * Parse command line arguments to get CSV file path and optional output path
 * @returns {Object} Object containing input and output file paths
 */
function parseArguments() {
  if (process.argv.length < 3) {
    throw new Error("Usage: node index.js <students.csv> [output.csv]");
  }

  const args = {
    inputCsv: process.argv[2],
    outputCsv: process.argv[3] || "filled_" + process.argv[2],
  };

  return args;
}

/**
 * Read and parse the CSV file
 * @param {string} filePath - Path to the CSV file
 * @returns {Promise<Array>} Array of student objects
 */
function readCSV(filePath) {
  return new Promise((resolve, reject) => {
    const results = [];
    fs.createReadStream(filePath)
      .on("error", (error) =>
        reject(new Error(`Failed to read CSV file: ${error.message}`)),
      )
      .pipe(csvParser())
      .on("data", (data) => results.push(data))
      .on("end", () => resolve(results))
      .on("error", (error) =>
        reject(new Error(`Failed to parse CSV file: ${error.message}`)),
      );
  });
}

/**
 * Read and parse the config.json file
 * @returns {Object} Configuration object
 */
function readConfig() {
  try {
    const configPath = path.join(process.cwd(), "config.json");
    const configData = fs.readFileSync(configPath, "utf8");
    return JSON.parse(configData);
  } catch (error) {
    throw new Error(`Failed to read config.json: ${error.message}`);
  }
}

/**
 * Check if branches exist for all students across all repositories
 * @param {Array} students - Array of student objects
 * @param {Array} repositories - Array of repository URLs
 * @returns {Promise<Array>} Array of student objects with branch status
 */
async function checkBranchesForAllStudents(students, repositories) {
  const results = [];
  let processedCount = 0;

  for (const student of students) {
    const studentResult = { ...student };

    console.log(
      `Processing student ${++processedCount}/${students.length}: ${student["English Name"]} (${student["Branch Name"]})`,
    );

    // Process repositories in batches to avoid too many simultaneous git operations
    const batchSize = 5;
    for (let i = 0; i < repositories.length; i += batchSize) {
      const batch = repositories.slice(i, i + batchSize);
      const checkPromises = batch.map(async (repo) => {
        const repoName = getRepoShortName(repo);
        const branchExists = await checkBranchExists(
          repo,
          student["Branch Name"],
        );
        studentResult[repoName] = branchExists ? 1 : 0;
      });

      await Promise.all(checkPromises);
    }

    results.push(studentResult);
  }

  return results;
}

/**
 * Extract a short name from repo URL for column headers
 * @param {string} repoUrl - Repository URL
 * @returns {string} Short name of the repository
 */
function getRepoShortName(repoUrl) {
  // Extract the last part of the URL (e.g., "c-1" from "https://gitee.com/kristenpire/c-1")
  return path.basename(repoUrl);
}

/**
 * Check if a branch exists in a repository
 * @param {string} repoUrl - Repository URL
 * @param {string} branchName - Branch name to check
 * @returns {Promise<boolean>} True if branch exists, false otherwise
 */
async function checkBranchExists(repoUrl, branchName) {
  try {
    // Use git ls-remote to check if the branch exists
    const command = `git ls-remote ${repoUrl} refs/heads/${branchName}`;
    const { stdout } = await execPromise(command);
    return stdout.trim() !== "";
  } catch (error) {
    console.error(
      `Error checking branch ${branchName} in ${repoUrl}: ${error.message}`,
    );
    return false;
  }
}

/**
 * Write results to a new CSV file
 * @param {Array} data - Array of student objects with branch status
 * @param {Array} repositories - Array of repository URLs
 * @param {string} outputFilePath - Path to the output CSV file
 * @returns {Promise<void>}
 */
async function writeCSV(data, repositories, outputFilePath) {
  // Create header for CSV writer
  const headers = [
    { id: "Chinese Name", title: "Chinese Name" },
    { id: "English Name", title: "English Name" },
    { id: "Branch Name", title: "Branch Name" },
  ];

  // Add repository columns
  repositories.forEach((repo) => {
    const repoName = getRepoShortName(repo);
    headers.push({ id: repoName, title: repoName });
  });

  // Create CSV writer
  const csvWriter = createObjectCsvWriter({
    path: outputFilePath,
    header: headers,
  });

  await csvWriter.writeRecords(data);
}

// Run the main function
main();
